﻿
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using NetCore.ORM.Query;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace NetCore.ORM.EF.Data
{
    public abstract class AbstractDbContext : DbContext, IAbstractDatabase, IQuery, IDbSource
    {
        #region EF基础
        public static Dictionary<string, List<dynamic>> _ConfigurationInstance = null;
        public abstract ProviderType CurrentProviderType { get; }

        protected string ConnectionString { get; set; }

        protected string Map_Card { get; set; }

      

        public DbProviderFactory DbFactory => throw new NotImplementedException();

      

        public AbstractDbContext()
        {
            ConnectionString = ApplicationEnvironments.Site.ConnectionString;
            Map_Card = ApplicationEnvironments.Site.AssemblyRule.ModelRule;
        }

        public AbstractDbContext(string connectionString)
        {
            ConnectionString = connectionString;
            Map_Card = ApplicationEnvironments.Site.AssemblyRule.ModelRule;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connectionString">连接字符串</param>
        /// <param name="assembly_card">dll通配符 如*.Model.dll</param>
        public AbstractDbContext(string connectionString,string map_card)
        {
            ConnectionString = connectionString;
            Map_Card = map_card;
        }

        // public DbSet<User> Users { get; set; }
        public AbstractDbContext(DbContextOptions<AbstractDbContext> options) : base(options)
        {
          
        }
       
        /// <summary>
        /// 创建映射关系
        /// </summary>
        /// <param name="builder"></param>
        protected override void OnModelCreating(ModelBuilder builder)
        {
            #region 动态加载模型实体
      
            base.OnModelCreating(builder);
          
            if (!string.IsNullOrWhiteSpace(Map_Card))
            {
                if (_ConfigurationInstance == null)
                {
                    _ConfigurationInstance = new Dictionary<string, List<dynamic>>();
                }
                List<dynamic> dynamics = null;
                if (_ConfigurationInstance.ContainsKey(Map_Card.ToLower()))
                {
                    dynamics = _ConfigurationInstance[Map_Card.ToLower()];
                }
                if (dynamics == null)
                {
                    dynamics = new List<dynamic>();
                    var lstRule= AppHttpContext.CacheService.GetCache<List<string>>(Map_Card);
                    if (lstRule != null && lstRule.Count > 0)
                    {
                        foreach (var dll in lstRule)
                        {
                            Regex regex = new Regex(ApplicationEnvironments.Site.AssemblyRule.ModelRule);
                            if (!regex.IsMatch(dll))
                            {
                                continue;
                            }
                            var typesToRegister = Assembly.LoadFrom(dll).GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null);
                            foreach (var type in typesToRegister)
                            {
                                dynamic configurationInstance = Activator.CreateInstance(type);
                                dynamics.Add(configurationInstance);
                                builder.ApplyConfiguration(configurationInstance);
                            }
                        }
                        if (_ConfigurationInstance.ContainsKey(Map_Card.ToLower()))
                        {
                            _ConfigurationInstance[Map_Card.ToLower()] = dynamics;
                        }
                        else
                        {
                            _ConfigurationInstance.Add(Map_Card.ToLower(), dynamics);
                        }
                    }
                    
                    return;
                }
                if (dynamics == null || dynamics.Count <= 0)
                {
                    return;
                }
                foreach (var type in dynamics)
                {
                    builder.ApplyConfiguration(type);
                }
            }
           
           

            #endregion

        }

       
        /// <summary>
        /// 初始化数据库
        /// </summary>
        /// <returns></returns>
        public bool DbEnsureCreated()
        {
            if (this.Database != null && this.Database.EnsureCreated())
            {
                return true;
                //object instance = ReflectionHelper.Create(ApplicationEnvironments.Site.InitData);
                //if (instance != null)
                //{
                //    var method = instance.GetType().GetMethod("InsertData", new Type[] { typeof(IDbRepository) });
                //    method.Invoke(instance, new object[] { this });
                //}
                ////_dbContext.Database.GenerateCreateScript();
            }
            return false;

        }
        #endregion

        //private readonly object locked = new object();
        /// <summary>
        /// 写入sql查询句
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="commandText">异常</param>
        public void WriteLog(string fileName, string commandText)
        {
            LogHelper.Info("sql-ef-base", commandText);
            // 系统里应该可以配置是否记录异常现象
            //if (!BaseSystemInfo.LogSQL)
            //{
            //    return;
            //}
            //lock (locked)
            //{
            //    // 将异常信息写入本地文件中"BaseSystemInfo.StartupPath" 
            //    string logDirectory = ApplicationEnvironments.BaseDirectory + @"\\Log\\Query";
            //    if (!System.IO.Directory.Exists(logDirectory))
            //    {
            //        System.IO.Directory.CreateDirectory(logDirectory);
            //    }
            //    string writerFileName = logDirectory + "\\" + fileName;
            //    File.AppendAllText(writerFileName, commandText);
            //}
        }

        #region EF中无效方法
        public IDbConnection Open()
        {
            return GetDbConnection();
        }

        public IDbConnection Open(string connectionString)
        {
            return GetDbConnection();
        }

        public IDbConnection GetDbConnection()
        {
            return this.Database.GetDbConnection();
        }

        public IDbTransaction GetDbTransaction()
        {
            return null;
        }
        #endregion

        #region Ado数据操作
        public IDataReader ExecuteReader(string commandText)
        {
            return this.ExecuteReader(commandText,null);
        }

        public IDataReader ExecuteReader(string commandText, DbParameter[] dbParameters)
        {
     
            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();
           
            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText, dbParameters);

                RelationalDataReader query = rawSqlCommand.RelationalCommand.ExecuteReader(this.Database.GetService<IRelationalConnection>(),
                    parameterValues: rawSqlCommand.ParameterValues);

                return query.DbDataReader;
            }
        }

        public IDataReader ExecuteReader(CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText, dbParameters);

                RelationalDataReader query = rawSqlCommand.RelationalCommand.ExecuteReader(this.Database.GetService<IRelationalConnection>(),
                    parameterValues: rawSqlCommand.ParameterValues);

                return query.DbDataReader;
            }
        }

        public int ExecuteNonQuery(string commandText)
        {
            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText);

                return rawSqlCommand.ExecuteNonQuery(this.Database.GetService<IRelationalConnection>());

            }
        }

        public int ExecuteNonQuery(string commandText, DbParameter[] dbParameters)
        {

            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText, dbParameters);

                int value = rawSqlCommand.RelationalCommand.ExecuteNonQuery(this.Database.GetService<IRelationalConnection>(),
                     parameterValues: rawSqlCommand.ParameterValues);

                return value;
            }
        }

        public int ExecuteNonQuery(CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            using (var cmd = this.GetConnection(this.ConnectionString).CreateCommand())
            {
                this.Database.OpenConnection();
                cmd.CommandText = commandText;
                cmd.CommandType = CommandType.Text;
                if (dbParameters != null && dbParameters.Length > 0)
                {
                    cmd.Parameters.AddRange(dbParameters);
                }

                return cmd.ExecuteNonQuery();
            }
        }

        public int ExecuteNonQuery(IDbTransaction transaction, CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            using (var cmd = transaction.Connection.CreateCommand())
            {
                cmd.CommandText = commandText;
                cmd.CommandType = CommandType.Text;
                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    foreach (var item in dbParameters)
                    {
                        cmd.Parameters.Add(item);
                    }
                }

                return cmd.ExecuteNonQuery();
            }
        }

        public object ExecuteScalar(string commandText)
        {
            return this.ExecuteScalar(commandText,null);
        }

        public object ExecuteScalar(string commandText, DbParameter[] dbParameters)
        {
            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText, dbParameters);

                object value = rawSqlCommand.RelationalCommand.ExecuteScalar(this.Database.GetService<IRelationalConnection>(),
                     parameterValues: rawSqlCommand.ParameterValues);

                return value;
            }
        }

        public object ExecuteScalar(CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            using(var cmd = this.GetConnection(this.ConnectionString).CreateCommand())
            {
                this.Database.OpenConnection();
                cmd.CommandText = commandText;
                cmd.CommandType = commandType;

                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    cmd.Parameters.AddRange(dbParameters);
                }
                return cmd.ExecuteScalar();
            }
        }

        public object ExecuteScalar(IDbTransaction transaction, CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            using (var cmd = transaction.Connection.CreateCommand())
            {
                cmd.CommandText = commandText;
                cmd.CommandType = commandType;

                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    foreach (var item in dbParameters)
                    {
                        cmd.Parameters.Add(item);
                    }
                }
                return cmd.ExecuteScalar();
            }
        }

        public int ExecuteProcedure(string procedureName)
        {
            return this.ExecuteProcedure(procedureName,null);
        }

        public int ExecuteProcedure(string procedureName, DbParameter[] dbParameters)
        {
            using (var cmd = this.GetConnection(this.ConnectionString).CreateCommand())
            {
                this.Database.OpenConnection();
                cmd.CommandText = procedureName;
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    cmd.Parameters.AddRange(dbParameters);
                }
                return  cmd.ExecuteNonQuery(); 
            }
        }

        public DataTable ExecuteProcedureForDataTable(string procedureName, string tableName, DbParameter[] dbParameters)
        {
            using (var cmd = this.GetConnection(this.ConnectionString).CreateCommand())
            {
                this.Database.OpenConnection();
                cmd.CommandText = procedureName;
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
              
                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    cmd.Parameters.AddRange(dbParameters);
                }
                var dbDataAdapter = GetDataAdapter();
                dbDataAdapter.SelectCommand = cmd;
                DataTable dataTable = new DataTable();
                dbDataAdapter.Fill(dataTable);
                dbDataAdapter.SelectCommand.Parameters.Clear();

                return dataTable;
            }
        }

        public IDbTransaction Begin()
        {
            var transaction = this.Database.BeginTransaction();
              return  transaction.GetDbTransaction();
        }

        public void Commit()
        {
            this.Database.CommitTransaction();
        }

        public void Rollback()
        {
            this.Database.RollbackTransaction();
        }

        public void Close()
        {
            //this.Database.CloseConnection();
        }

        public void WriteLog(string commandText)
        {
            throw new NotImplementedException();
        }

        public DbConnection GetConnection(string connectionString)
        {
            return this.Database.GetDbConnection();
        }

        public DbDataAdapter GetDataAdapter()
        {
            throw new NotImplementedException();
        }

        public abstract int SelectIdentity(string tableName);


        public abstract DateTime Now();

        public abstract DbParameter[] GetParameter(List<DataParameter> parameters);

        public abstract DbParameter GetParameter(string name, object value);

        public abstract string GetParameter(string name);

        public abstract string GetLikeParameter(string parameter);

        public abstract string Identity(string where);

        public abstract void SqlBulkCopyData(DataTable dataTable);

        public abstract string Identity_Disabled(string tableName);

        public abstract string Identity_Enable(string tableName);

        public abstract DbParameter MakeOutParam(string paramName, DbType DbType, int Size);

        public abstract DbParameter MakeInParam(string paramName, DbType DbType, int Size, object Value);

        public abstract DbParameter MakeParam(string paramName, DbType DbType, int Size, ParameterDirection Direction, object Value);

        public List<T> GetReader<T>(string sql, DataParameterCollection dataParam) where T : new()
        {
            List<T> lstT = new List<T>();
            var dbReader = this.ExecuteReader(sql, dataParam.ToArray());
            try
            {
                while (dbReader.Read())
                {
                    var entity = DbFactoryHelper.ReaderToEntity<T>(dbReader);
                    if (entity != null)
                    {
                        lstT.Add(entity);
                    }

                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                dbReader.Close();
            }
            return lstT;
        }

        #endregion

        
       

        #region IQuery

        public abstract string SelectAll(DbFactoryHelper entity);


        public abstract string RecordCount(DbFactoryHelper entity);

        public abstract string RecordCount(string hql);


        public abstract string QueryPage(DbFactoryHelper entity, Order[] order, int pageIndex, int pageSize);


        public abstract string QueryPage(string hql, Order[] orders, int pageIndex, int pageSize);
        #endregion

        #region IDbSource
        public abstract string GetTable(string DBName);

        public abstract string GetView(string DBName);

        public abstract string GetDbColumn(string tableName, string dbServerName, bool IsTable);

        public abstract string GetSource(string tableName);


        public abstract string CreateTable(string table, List<DbColumnAttribute> dbColumns);

        public abstract string AddColumn(string table, DbColumnAttribute dbColumns);

        public abstract string DeleteTable(string table);

        public abstract string DeleteColumn(string table, string columnName);

        #endregion
    }
}